/**
 * @file router.ts
 * @author shenyc
 * @description 路由
 */
import { createRouter, RouteRecordRaw, createWebHashHistory } from "vue-router";
import { constRouter } from "./modules/const-router";
import { errorRouter } from "./modules/const-router";
import { hookProgress } from "@/hooks/hookProgress";
import { GlobalConfig, RouterConfig } from "@/config";
import { useAuthStore } from "@/store";
import { useUserStore } from "@/store";
import { useTabsStore } from "@/store";
import { ROOT_NAME, LOGIN_NAME, LAYOUT_NAME } from "@/config/modules/router";

/**
 * 创建路由
 */
export const router = createRouter({
    history: createWebHashHistory(),
    strict: false, // 尾部斜杠
    sensitive: false, // 大小写敏感
    routes: [...constRouter, ...errorRouter],
    scrollBehavior: (to, from, savedPosition) => {
        if (savedPosition) {
            return savedPosition;
        } else {
            return { left: 0, top: 0 };
        }
    },
});

/**
 * 注册路由导航守卫
 *
 * [完整的导航解析流程]
 *  1.导航被触发。
 *  2.在失活的组件里调用 beforeRouteLeave 守卫。
 *  3.调用全局的 beforeEach 守卫。
 *  4.在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
 *  5.在路由配置里调用 beforeEnter。
 *  6.解析异步路由组件。
 *  7.在被激活的组件里调用 beforeRouteEnter。
 *  8.调用全局的 beforeResolve 守卫(2.5+)。
 *  9.导航被确认。
 * 10.调用全局的 afterEach 钩子。
 * 11.触发 DOM 更新。
 * 12.调用 beforeRouteEnter 守卫中传给 next 的回调函数，创建好的组件实例会作为回调函数的参数传入。
 */
export const registerRouter = () => {
    // 进度条
    const nprogress = hookProgress();
    // 应用标题
    const APP_TITLE = GlobalConfig.APP_TITLE;
    // 登录 URL
    const LOGIN_URL = RouterConfig.LOGIN_URL;
    // 不重定向白名单 (白名单必须在 constRouter 中定义， 这里是 '/500')
    const ROUTER_WHITE_LIST = RouterConfig.ROUTER_WHITE_LIST;

    /**
     * 全局前置守卫 - router.beforeEach
     * 注意：确保 next 在任何给定的导航守卫中都被严格调用一次
     *
     * 在前端路由跳转中，路由跳转前都是会经过beforeEach，而beforeEach可以通过next来控制到底去哪个路由。
     * 根据这个特性我们就可以在beforeEach中设置一些条件来控制路由的重定向。
     *
     * 常见的使用场景有：
     * 1. 验证用户是否登录（若未登录，且当前非登录页面，则自动重定向登录页面）
     * 2. 用户权限
     * 3. 用户输入的路路径是否存在，不存在的情况下如何处理，重定向到哪个页面。
     */
    router.beforeEach(async (to, from, next) => {
        // console.log(`from = ${from.path}`);
        // console.log(`to = ${to.path}`);

        const authStore = useAuthStore();
        const userStore = useUserStore();
        const tabsStore = useTabsStore();

        // 开始
        nprogress.start();

        // 动态设置标题
        const app_title = APP_TITLE;
        document.title = to.meta.title ? `${to.meta.title} - ${app_title}` : app_title;

        // 动态设置标签标题
        tabsStore.tabsMenuList.forEach((item) => {
            let flatMenu = authStore.FlatMenuList.find((p) => p.name == item.name);
            if (flatMenu) {
                item.title = flatMenu.meta?.title;
            }
        });

        // 1.判断是访问登陆页，有 Token 就在当前页面，没有 Token 重置路由到登陆页
        if (LOGIN_URL === to.path.toLocaleLowerCase()) {
            // console.log(`3.判断是访问登陆页，有 Token 就在当前页面，没有 Token 重置路由到登陆页`);
            if (userStore.token) {
                return next(from.fullPath);
            } else {
                resetRouter();
                return next();
            }
        }

        // 2.判断访问页面是否在路由白名单地址(静态路由)["/500"]中，如果存在直接放行
        if (ROUTER_WHITE_LIST.includes(to.path)) {
            // console.log(`4.判断访问页面是否在路由白名单地址(静态路由)中，如果存在直接放行`);
            return next();
        }

        // 3.判断是否有 Token，没有则重定向到 login 页面
        // userStore.setToken("")
        if (!userStore.token) {
            // console.log(`5.判断是否有 Token，没有重定向到 login 页面`);
            next({ path: LOGIN_URL, replace: true });
            return;
        }

        // 4.如果有令牌但没有菜单列表，就重新请求菜单列表并添加动态路由
        if (!authStore.menuList.length) {
            // console.log(`6.如果没有菜单列表，就重新请求菜单列表并添加动态路由`);
            await initDynamicRouter();
            return next({ ...to, replace: true });
        }

        // 5.存储 routerName 做按钮权限筛选
        // console.log(`7.存储 routerName 做按钮权限筛选 = ${to.name as string}`);
        authStore.setRouteName(to.name as string);

        // 6.正常访问页面
        // console.log(`8.正常访问页面`);
        next();
    });

    /**
     * router.beforeResolve - 全局解析守卫
     * 你可以用 router.beforeResolve 注册一个全局守卫。
     * 这和 router.beforeEach 类似，因为它在 每次导航时都会触发，
     * 但是确保在导航被确认之前，同时在所有组件内守卫和异步路由组件
     * 被解析之后，解析守卫就被正确调用。
     */
    // router.beforeResolve(async (to, from, next) => {
    //     console.log(`in router.beforeResolve ...`)
    //     next();
    // });

    /**
     * router.afterEach - 全局后置钩子
     * 你也可以注册全局后置钩子，然而和守卫不同的是，
     * 这些钩子不会接受 next 函数，也不会改变导航本身：
     */
    router.afterEach(async (to, from, failure) => {
        nprogress.done();
    });

    /**
     * router.onError
     * 路由错误
     */
    router.onError((error) => {
        console.log(`路由错误 -- ${error.message}`);
        nprogress.done();
    });
};

/**
 * 重置路由
 */
export const resetRouter_sfx = () => {
    // 重置路由白名单
    const resetWhiteNameList = [ROOT_NAME, LOGIN_NAME, LAYOUT_NAME, "403", "404", "500"];
    router.getRoutes().forEach((item) => {
        const { name } = item;
        if (name && !resetWhiteNameList.includes(name as string)) {
            router.hasRoute(name) && router.removeRoute(name);
        }
    });
};

/**
 * 把名称相同的路由删除
 */
export const resetRouter_geeker = () => {
    const authStore = useAuthStore();
    authStore.FlatMenuList.forEach((route) => {
        const { name } = route;
        if (name && router.hasRoute(name)) router.removeRoute(name);
    });
};

export const resetRouter = resetRouter_sfx;

/**
 * 📚 初始化动态路由
 */
export const initDynamicRouter = async () => {
    //
    const authStore = useAuthStore();
    const userStore = useUserStore();

    const LOGIN_URL = RouterConfig.LOGIN_URL;
    const LAYOUT_NAME = RouterConfig.LAYOUT_NAME;

    // 使用 vite import.meta.glob
    // 引入 views 文件夹下所有 vue 文件
    const modules = import.meta.glob("@/views/**/*.vue");

    try {
        // 构造菜单列表 - menuList (暂时时使用本地动态菜单)
        await authStore.buildMenuList();
        // 获取按钮列表
        // await authStore.getAuthButtonList();

        // 判断当前用户有没有菜单权限
        if (!authStore.menuList.length) {
            // ElNotification({
            //     title: "无权限访问",
            //     message: "当前账号无任何菜单权限，请联系系统管理员！",
            //     type: "warning",
            //     duration: 3000
            // });

            userStore.setToken("");
            router.replace(LOGIN_URL);
            return Promise.reject("No permission");
        }

        // 添加动态路由
        authStore.FlatMenuList.forEach((item) => {
            item.children && delete item.children;
            if (item.component && typeof item.component == "string") {
                item.component = modules["/src/views" + item.component + ".vue"];
            }
            if (item.meta.isFull) {
                router.addRoute(item as RouteRecordRaw);
            } else {
                router.addRoute(LAYOUT_NAME, item as RouteRecordRaw);
            }
        });
    } catch (error) {
        // 当按钮 || 菜单请求出错时，重定向到登陆页
        userStore.setToken("");
        router.replace(LOGIN_URL);
        return Promise.reject(error);
    }

    return;
};
